/**
 * class Node
 */

package nwsim;

import java.util.*;

public class Node extends Thread{
	class PktGenerator extends TimerTask {
		
		public PktGenerator() {
			int pktDelay = 0, pktInterval = 0;
			if(nodeID == 0) {
				pktDelay = 2; 	pktInterval = 5; 	count = 10;
			}
			else if(nodeID == 1) {
				pktDelay = 2; 	pktInterval = 50; 	count = 0; //no significance of count
			}
			else if(nodeID == 2) {
				pktDelay = 2; 	pktInterval = 5; 	count = 10;
			}
			Timer pktTimer = new Timer();
			pktTimer.schedule(this, pktDelay, pktInterval);
		}
		
		public void run() {
			seqNo++;
			if(nodeID == 0) {
				sendPacket(0, 1, seqNo, 1, Message.MessageType.MSG_NATIVE);
			}
			else if(nodeID == 1) {
				if(!(f1Queue.isEmpty() || f2Queue.isEmpty())) {	//both queues have pkts, perform coding
					Message codedmsg = new Message();
					codedmsg.msgType = Message.MessageType.MSG_CODED;
					codedmsg.src = 1;
					codedmsg.dest = Constants.broadcastAddr;
					codedmsg.flowID = Constants.notAFlow;
					codedmsg.msgSeqNo = 0; //don't care
					codedmsg.f1SeqNo = f1Queue.remove().msgSeqNo;
					codedmsg.f2SeqNo = f2Queue.remove().msgSeqNo;
					NwSim.getChannel().sendToChannel(codedmsg);
					NwSim.ETX++;
					idle = 0;
				}
				/*else if(!f1Queue.isEmpty()) { //f1 native possible
					Message msgFromF1Queue = f1Queue.remove();
					sendPacket(1, 2, msgFromF1Queue.msgSeqNo, 1, Message.MessageType.MSG_NATIVE);
					idle = 0;
				}
				else if(!f2Queue.isEmpty()) { //f2 native possible
					Message msgFromF2Queue = f2Queue.remove();
					sendPacket(1, 0, msgFromF2Queue.msgSeqNo, 2, Message.MessageType.MSG_NATIVE);
					idle = 0;
				}*/
				/*else if(f1Queue.isEmpty() && f2Queue.isEmpty()){ //both queues are empty, abort after some time
					idle++;
					System.out.println("Router idle for " + idle);
					if(idle == 100) {
						System.out.println("Stopping simulation..."); //TODO: print results here
						NwSim.stopSimulation();
					}
				}*/
			}
			else if(nodeID == 2) {
				sendPacket(2, 1, seqNo, 2, Message.MessageType.MSG_NATIVE);
			}
			
			if((nodeID == 0 || nodeID == 2) && (seqNo == count)) {
				this.cancel();
			}
		}
		
		int seqNo = 0;
		int count = 0;
		int idle = 0;
	}
			
	public int nodeID;
	public Coordinates nodePosition; 
	public ArrayList nbrTbl;
	int destSeqNo = 0;
	public boolean stopThread = false;
	
	public Queue<Message> f1Queue, f2Queue;
	public PktGenerator transmitEngine;
	
	public Node(int id, Coordinates pos) {
		nodeID = id;
		nodePosition = pos;
		nbrTbl = new ArrayList();
		f1Queue = new LinkedList<Message>();
		f2Queue = new LinkedList<Message>();
	}
	
	public void run() {
		transmitEngine = new PktGenerator(); //launch transmit engine

		while(true) {
			Message recvdMsg;
			synchronized(NwSim.getChannel()) {
				try {
					NwSim.getChannel().wait();
				}
				catch (InterruptedException ex) {
					return;
				}
				
				recvdMsg = NwSim.getChannel().recvFromChannel();
			}
			if(recvdMsg != null) {
				for(int i=0; i<nbrTbl.size(); i++) {
					if(((Neighbor)nbrTbl.get(i)).nodeID == recvdMsg.src) { //check if src is my neighbor
						handlePacket(recvdMsg);
						break;
					}
				}
			}
			if(stopThread == true) {
				return;
			}
		}
	}
	
	void handlePacket(Message msg) {
		switch(nodeID) {
			
			case 0: {
				if(msg.msgType == Message.MessageType.MSG_CODED) {
					if(msg.f2SeqNo == destSeqNo + 1) {
						System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
						+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.f2SeqNo);
						destSeqNo++; //yaay, i decoded it!
					}
				}
				else if(msg.msgType == Message.MessageType.MSG_NATIVE) {
					if(msg.flowID == 2)
						if(msg.msgSeqNo == destSeqNo + 1) {
							System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
							+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.msgSeqNo);
							destSeqNo++; //yaay, dumbass router sent me a native packet
						}
				}
				if(destSeqNo == 10) {
					NwSim.stopSimulation();
				}
			}
			break;
			
			case 1: {
				if(msg.msgType == Message.MessageType.MSG_NATIVE) { //just confirming
					if(msg.flowID == 1) {
						System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
						+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.msgSeqNo);
						f1Queue.add(msg);
					}
					else if(msg.flowID == 2) {
						System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
						+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.msgSeqNo);
						f2Queue.add(msg);
					}
				}
			}
			break;
			
			case 2: {
				if(msg.msgType == Message.MessageType.MSG_CODED) { 
					if(msg.f1SeqNo == destSeqNo + 1) {
						System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
						+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.f1SeqNo);
						destSeqNo++; //yaay, i decoded it!
					}
				}
				else if(msg.msgType == Message.MessageType.MSG_NATIVE) {
					if(msg.flowID == 1)
						if(msg.msgSeqNo == destSeqNo + 1) {
							System.out.println("Node " + nodeID + " received a message of flow " + msg.flowID + " from " 
							+ msg.src + " of type " + msg.msgType.toString() + " and seq no " + msg.msgSeqNo);
							destSeqNo++; //yaay, dumbass router sent me a native packet
						}
				}
				if(destSeqNo == 10) {
					NwSim.stopSimulation();
				}
			}
			break;
			
			default: System.out.println("What the hell"); break;
		}
	}

	public void addNeighbor(int id, double prr) {
		Neighbor nbr = new Neighbor();
		nbr.nodeID = id;
		nbr.prr = prr;
		nbrTbl.add(nbr);
	}
	
	public void sendPacket(int src, int dest, int seqNo, int flowID, Message.MessageType msgType) {
		Message newpkt = new Message();
		newpkt.msgType = msgType;
		newpkt.src = src;
		newpkt.dest = dest;
		newpkt.flowID = flowID;
		newpkt.msgSeqNo = seqNo;
		NwSim.getChannel().sendToChannel(newpkt);
		NwSim.ETX++;
	}
	
}

class Neighbor {
	public int nodeID;
	public double prr;
}

